package pokerantani.v2;

import java.io.*;
import java.util.Arrays;
import java.util.List;

import pokerantani.v2.model.Card;


/**
 * Poker Hand detector
 * for use with Poker Hand Dataset from UCI[1]
 * [1]: http://archive.ics.uci.edu/ml/machine-learning-databases/poker/
 * 
 * @author Federico Ferri
 *
 */

public class HandDetector {
	static void printUsage() {
		System.out.println("HandDetector <pokerHandDataSet>");
	}
	
	static Card[] extractHand(String line) {
		String s[] = line.split(",");
		return new Card[]{
				new Card(Integer.parseInt(s[1]), Integer.parseInt(s[0])),
				new Card(Integer.parseInt(s[3]), Integer.parseInt(s[2])),
				new Card(Integer.parseInt(s[5]), Integer.parseInt(s[4])),
				new Card(Integer.parseInt(s[7]), Integer.parseInt(s[6])),
				new Card(Integer.parseInt(s[9]), Integer.parseInt(s[8]))
		};
	}
	
	static String hand2str(Card[] c) {
		StringBuilder sb = new StringBuilder();
		boolean flip = true;
		for(Card c0 : c) {
			if(flip) flip = false;
			else sb.append(" ");
			sb.append(c0.toString());
		}
		return sb.toString();
	}
	
	static int detectHand(Card c[]) {
		Arrays.sort(c);
		boolean isFlush = isFlush(c);
		boolean isStraight = isStraight(c);
		boolean isHighStraight = isHighStraight(c);
		int count[] = NofAKind(c);
		if(isFlush && isHighStraight) return 9; /* royal flush */
		if(isFlush && isStraight) return 8; /* straight flush */
		if(count[0] == 4) return 7; /* four of a kind */
		if(count[0] == 3 && count[1] == 2) return 6; /* full house */
		if(isFlush) return 5; /* flush */
		if(isStraight || isHighStraight) return 4; /* straight */
		if(count[0] == 3) return 3; /* three of a kind */
		if(count[0] == 2 && count[1] == 2) return 2; /* two pairs */
		if(count[0] == 2) return 1; /* one pair */
		else return 0; /* nothing in hand */
	}
	
	public static int detectHand(List<Card> c) {
		Card cards[] = new Card[c.size()];
		c.toArray(cards);
		return detectHand(cards);
	}
	
	public static String handToString(int h) {
		switch(h) {
		case 0: return "nothing";
		case 1: return "one pair";
		case 2: return "two pairs";
		case 3: return "three of a kind";
		case 4: return "straight";
		case 5: return "flush";
		case 6: return "full house";
		case 7: return "four of a kind";
		case 8: return "straight flush";
		case 9: return "royal flush";
		}
		return "???";
	}
	
	static boolean isFlush(Card c[]) {
		for(Card ci : c) if(ci.suit != c[0].suit) return false;
		return true;
	}
	
	static boolean isStraight(Card c[] /* sorted */) {
		for(int i = 1; i < c.length; i++) if(c[i].rank - i != c[0].rank) return false;
		return true;
	}
	
	static boolean isHighStraight(Card c[] /* sorted */) {
		if(c[0].rank != 1 || c[c.length - 1].rank != 13) return false;
		for(int i = 2; i < c.length; i++) if(c[i].rank - i != c[1].rank - 1) return false;
		return true;
	}
	
	static int[] NofAKind(Card c[]) {
		int count[] = new int[1+13]; // index 0 not used
		for(Card c0 : c) count[c0.rank]++;
		Arrays.sort(count);
		return new int[]{count[count.length - 1], count[count.length - 2]};
	}
	
	public static void main(String[] args) throws IOException {
		if(args.length != 1) {
			printUsage();
			System.exit(0);
		}
		
		FileReader r = new FileReader(new File(args[0]));
		BufferedReader br = new BufferedReader(r);
		String line;
		Card c[];
		int h, t;
		String s1, s2;
		while((line = br.readLine()) != null) {
			c = extractHand(line);
			s1 = hand2str(c);
			h = Integer.parseInt(line.substring(line.lastIndexOf(',') + 1));
			t = detectHand(c);
			s2 = hand2str(c);
			System.out.println(line + " -> " + s1 + " -> " + s2 + " -> " + t + " " + (h == t ? "[OK]" : "[ERROR]"));
		}
	}
}
